GO
1. Expect介绍
1.1. 什么是Expect
Expect是一个用来实现自动交互功能的软件套件(Expect is a softeare suite for automating interactive tools,这是作者的定义),是基于TCL(全拼为Tool Command Language,是一种脚本语言,由John Ousterhout创建。TCL功能很强大,经常被用于快速原型开发、脚本编程、GUI和测试等方面,不过现在用得不多了)的脚本编程工具语言,方便学习,功能强大。
1.2. 为什么要使用Expect
在现今的企业运维中,自动化运维已经称为运维的主流趋势,但是在很多情况下,执行系统命令或程序时,系统会以交互式的形式要求运维人员输入指定的字符串,之后才能继续执行命令。例如,为用户设置密码时,一般情况下就需要手工输入2次密码;再比如使用SSH远程连接服务器时,第一次连接要和系统实现两次交互式输入。
简单的说,Expect就是用来自动实现与交互式程序通信的,而无需管理员的手工干预。比如SSH、FTP远程连接等,正常情况下都需要手工与它们进行交互,而使用Except就可以模拟手工交互的过程,实现自动与远端程序的交互,从而达到自动化运维的目的。
以下是Except的自动交互工作流程的简单说明,依次执行如下操作:
spawn
启动指定进程—->expect
获取期待的关键字—->send
向指定进程发送指定字符—->进程执行完毕,退出结束。
2. 安装Expect软件
首先,要确保机器可以正常上网,并设置好yum安装源,然后执行yum install expect -y
命令安装Except软件,安装过程如下:
3. 小试牛刀:实现Expect自动交互功能
编写theshu.exp #<==扩展名使用exp代表是Expect脚本
12345#!/usr/bin/expect #<==脚本开头解释器,和Shell类似,表示程序使用Expect解析spawn ssh root@192.168.33.130 uptime #<==实行ssh命令(注意开头必须要有spawn,否则无法实现交互)expect "*password" #<==利用Except获取执行上述ssh命令输出的字符串是否为期待的字符串*password,这里的*是通配符send "123456\n" #<==当获取到期待的字符串*password时,则发送12345密码给系统。\n为换行。expect eof #<==处理完毕后结束Except执行Expect:
expect theshu.exp
4. Expect程序自动交互的重要命令及实践
Expect程序中的命令是Expect的核心,需要重点掌握。
4.1. spawn命令
在Expect自动交互程序执行的过程中,spawn
命令是一开始就需要使用的命令,通过spawn
执行一个命令或程序,之后所有的Expect操作都会在这个执行过的命令或程序进程中进行,包括自动交互功能,因此如果没有spawn
命令,Expect程序将会无法实现自动交互。
spawn
命令的语法为:spawn [选项] [需要自动交互的命令或程序]
例如:spawn ssh root@192.168.33.130 uptime
在spawn
命令的后面,直接加上要执行的命令或程序(例如这里的ssh命令)等,除此之外,spawn
还支持如下一些选项:
-open
:表示启动文件进程-ignore
:表示忽略某些信号- 提示:这些选项不常用,了解即可,无需深入。
使用spawn
命令是Expect程序实现自动交互工作流程中的第一步,也是最关键的一步。
4.2. expect命令
4.2.1. expect命令语法
在Expect自动交互程序的执行过程中,当使用spawn
命令执行一个命令或程序之后,会提示某些交互式信息,expect
命令的作用就是获取spawn
命令执行后的信息,看看是否和其事先指定的相匹配,一旦匹配上指定的内容就执行expect
后面的动作,expect
命令也有一些选项,相对用得多的是-re
,表示使用正则表达式的方式来匹配。
expect命令的语法为:expect 表达式 [动作]
示例如下:
注意,上述命令不能直接在Linux命令行中执行,需要放入Expect脚本中执行。
4.2.2. expect命令的实践
范例1:执行ssh命令远程获取服务器负载值,并要求实现自动输入密码。
- 方法1:将
expect
和send
放在一行:1234567891011[root@theshu ~]# which expect/bin/expect #<==expect程序的绝对路径[root@theshu ~]# cat test1.exp#!/bin/expect #<==指定expect解释器spawn ssh theshu@67.216.210.110 -p 29643 uptime #<==开启expect自动交互式,执行ssh命令expect "*password" {send "theshu..\n"} #<==如果ssh命令输出匹配*oassword,就发送theshu..给系统expect eof #<==要想输出结果,还必须加eof,表示expect结束[root@theshu ~]# expect test1.exp #<==采用expect执行脚本,就相当于使用sh执行Shell脚本spawn ssh theshu@67.216.210.110 -p 29643 uptimetheshu@67.216.210.110's password: #<==Expect程序自动帮我们输入了密码07:56:00 up 33 days, 1:39, 0 users, load average: 0.00, 0.00, 0.00
从上面的例子可以看出,expect
命令是依附于spawn
命令的,即通过spawn
执行ssh命令后,系统会提示输入密码,此时的expect命令按照事先的配置匹配ssh命令执行后的字符串password,如果匹配到了指定的passeord字符串,则会执行紧随其后包含在大括号中的send
或exp_send
动作,匹配的动作也可以放在下一行,这样就不需要使用大括号了,就像下面这样,实际完成的功能与上面的一样。
- 方法2:
expect
和send
放在不同行12345678910[root@theshu ~]# cat test2.exp#!/bin/expectspawn ssh theshu@67.216.210.110 -p 29643 uptimeexpect "*password"send "theshu..\n"expect eof[root@theshu ~]# expect test2.expspawn ssh theshu@67.216.210.110 -p 29643 uptimetheshu@67.216.210.110's password:08:04:38 up 33 days, 1:47, 0 users, load average: 0.00, 0.00, 0.00
expect
命令还有一种高级用法,即它可以在一个expect
匹配中多次匹配不同的字符串,并给出不通的处理动作,此时只需要将匹配的所有字符串放在一个大括号里就可以了,当然还要借助exp_continue
指令实现继续匹配。
范例3:执行ssh命令远程获取服务器负载值,并自动输入yes
及用户密码。
说明:
exp_send
和sed
类似,后面的\r(回车)
和前文的\n(换行)
类似。expect{}
,类似多行expect
。- 匹配多个字符串,需要在每次匹配并执行动作后,加上
exp_continue
4.3. send命令
send
和exp_send
这两个命令是Expect中的动作命令,用法类似,即在expect
命令匹配指定的字符串后,发送指定的字符串给系统,这些命令可以支持一些特殊的转义符号。例如:\r
表示回车、\n
表示换成、\t
表示制表符等,这些用法与TCL中的特殊符号相同。
示例如下:
send
命令有几个可用的参数,具体如下。
-i
:指定spawn_id,用来向不通的spawn_id进程发送命令,是进行多程序控制的参数。-s
:s代表slowly,即控制发送的速度,使用的时候要与expect中的变量send_slow相关联。
4.4. exp_continue命令
exp_continue
命令,一般处于expect
命令中,属于一种动作命令,一般用在匹配多次字符串的动作中,从命令的拼写就可以看出命令的作用,即让Expect程序继续匹配的意思,示例如下:
说明:如果需要一次匹配多个字符串,那么不同的匹配之间就要加上exp_continue
,否则expect将不会自动输入指定的字符串。最后一个的结尾就不需要加上exp_continue
了,因为都匹配完成了。
在这个例子中,匹配第一个字符串”username”之后,expect发送”theshu”字符串给系统,然后利用exp_continue继续匹配下一个字符串”*pass*“,并自动输入指定字符串”123456”,最后匹配”*mail*“,并输入指定字符串”theshu@qq.com”,由于结尾没有exp_continue,因此匹配结束。
4.5. send_user命令
send_user
命令可以用来打印Expect脚本信息,类似Shell里的echo命令,而默认的send
、exp_send
命令都是将字符串输出到Expect程序中去,有关send_user
命令用法的示例如下:
通过上述例子可以看出,它很像Shell里的echo命令。而且有
echo -e
的功能。
4.6. exit命令
exit
命令的功能类似于Shell中的exit
,即直接退出Expect脚本,除了最基本的退出脚本功能之外,还可以利用这个命令对脚本做一些关闭前的清理和提示等工作,比如下面的示例:
4.7. Expect常用命令总结
下面将上面所说的知识进行总结以方便记忆,整理结果如下标:
Expect命令 | 作用 |
---|---|
spawn | spawn命令是一个在Expect自动交互程序的开始就需要使用的命令,通过spawn执行一个命令或程序,之后所有的Expect操作都在这个执行过的命令或程序进程中进行,包括自动交互功能 |
expect | 在Expect自动交互程序的执行过程中,在使用spawn命令执行一个命令或程序之后,会提示某些交互式信息,expect命令的作用就是获取这些信息,查看是否和其事先指定的信息相匹配,一旦匹配上指定的内容,就执行expect后面的动作 |
send | Expect中的动作命令,当expect匹配了指定的字符串后,发送指定的字符串给系统,这些命令可以支持一些特殊的转义符号,例如\r表示回车,\n表示换行,\t表示制表符等,还有一个类似的exp_send 命令 |
exp_continue | 属于一种动作命令,在一个expect命令中,用于多次匹配字符串并执行不同的动作中。该命令的作用就是让expect程序继续匹配 |
send_user | send_user命令用来打印Expect脚本信息,类似Shell里的echo命令,并且带上-e选项的功能 |
exit | 退出Expect脚本,以及在退出脚本前做一些关闭前的清理和提示等工作 |
5. Expect程序变量
5.1. 普通变量
Expect中的变量定义、使用方法与TCL语言中的变量基本相同。
定义变量的基本语法如下:set 变量名 变量值
。例如:set password "123456"
打印变量的基本语法如下:puts $变量名
或send_user "$变量名\n"
范例:定义及输出变量
5.2. 特殊参数变量
在Expect里也有与Shell脚本里的$0、$1、$#等类似的特殊参数变量,用于接收及控制Expect脚本传参。
在Expect中$argv
表示参数数组,可以使用[lindex $argv n]
接收Expect脚本传参,n从0开始,分别表示第一个[lindex $argv 0]
参数、第二个[lindex $argv 1]
参数、第三个[lindex $argv 2]
参数……
范例1:定义及输出特殊参数变量:
Expect接收参数的方式和bash脚本的方式有些区别,bash是通过$0…$n这种方式来接受的,而Expect是通过set 变量名称 [lindex $argv <param index>]
来接收的,例如:set file [lindex $argv 0]
。
除了基本的位置参数外,Expect也支持其他的特殊参数,例如:$argc
表示传参的个数,$argv0
表示脚本的名字。
范例2:针对Expect脚本传参的个数及脚本名参数的实践
6. Expect程序中的if条件语句
Expect程序中if条件语句的基本语法为:
或
说明:
if
关键字后面要有空格,else
关键字前后都要有空格,{条件表达式}
大括号里面靠近大括号处可以没有空格,将指令括起来的起始大括号{
前面要有空格。
范例1:使用if语句判断脚本传参的个数,如果不符则给予提示
范例2:使用if语句判断脚本传参的个数,不管是否符合都给予提示。
这篇学习笔记的目的并不是用来彻底精通Expect语言,而是指导读者解决运维管理中的交互问题,实现自动化运维,因此,不要过多地纠结于Expect语言,而应该多关注自动化交互的知识。
7. Expect中的关键字
Expect中的特殊关键字用于匹配过程,代表某些特殊的含义或状态,一般只用于Expect命令中而不能在Expect命令外面单独使用。
7.1. eof关键字
eof(end-of-file)
关键字用于匹配结束符。例如:
再比如:
7.2. timeout关键字
timeout
是Expect中的一个控制时间的关键字变量,它是一个全局性的时间控制开关,可以通过为这个变量赋值来规定整个Expect操作的时间,注意这个变量是服务于Expect全局的,而不是某一条命令,即使命令没有任何错误,到了时间仍然会激活这个变量,此外,到时间后还会激活一个处理及提示信息开关,下面来看看它的实际使用方法。
范例:timeout超时功能实践。
上面的处理中,首先将timeout变量设置为30秒,此时Expect脚本的执行只要超过了30秒,就会直接执行结尾的timieout动作,打印一个信息,停止运行脚本。还可以做更多的其他事情。
在expect{}的用法中,还可以使用下面的timeout语法:
timeout变量设置为0,表示立即超时,为-1则表示永不超时。
8. 小结
Expect程序的功能远不止文本介绍的这些,本篇笔记主要从运维工作实战的角度,来讲解自动化运维中用Shell脚本难以实现的交互式问题的解决方案。对于一般的企业运维人员,掌握以上的这些就已经够用了。类似的交互工具还有sshpass、ansible等。
OK